// This file is a part of Funky C++.
// Copyright (C) 2011  Valentin David
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
#ifndef CALLBACK_WRAPPER_HH
# define CALLBACK_WRAPPER_HH

# include <memory>

namespace jack {
  template <typename Ret, typename T, typename... Args>
  struct as_function_const {
  private:
    typedef Ret (T::*type)(Args...) const;
    const T* obj;
    type f;

  public:
    as_function_const(const as_function_const&) = default;
    as_function_const(const T* obj, type f): obj(obj), f(f) {}

    template <typename... A,
	      typename = typename std::enable_if<
		sizeof...(A) == sizeof...(Args)
		>::type>
    Ret operator()(A... args) const {
      return (obj->*f)(std::forward<A>(args)...);
    }
  };

  template <typename Ret, typename T, typename... Args>
  struct as_function_nonconst {
  private:
    typedef Ret (T::*type)(Args...);
    T* obj;
    type f;

  public:
    as_function_nonconst(const as_function_nonconst&) = default;
    as_function_nonconst(T* obj, type f): obj(obj), f(f) {}

    template <typename... A,
	      typename = typename std::enable_if<
		sizeof...(A) == sizeof...(Args)
		>::type>
    Ret operator()(A... args) const {
      return (obj->*f)(std::forward<A>(args)...);
    }
  };

  template <typename Ret, typename T, typename... Args>
  as_function_const<Ret, T, Args...> fun(const T* obj,
					 Ret (T::*f)(Args...) const) {
    return as_function_const<Ret, T, Args...>(obj, f);
  }

  template <typename Ret, typename T, typename... Args>
  as_function_nonconst<Ret, T, Args...> fun(T* obj, Ret (T::*f)(Args...)) {
    return as_function_nonconst<Ret, T, Args...>(obj, f);
  }

}

#endif
